package stone926.mods.more_enchantments.mixins;

import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.enchantments.TenThousandArrowsEnchantment;
import stone926.mods.more_enchantments.enchantments.legendary.StableEnchantment;
import stone926.mods.more_enchantments.enchantments.negative.TheTargetOfAllEnchantment;
import stone926.mods.more_enchantments.enums.ArrowSpawnPosition;
import stone926.mods.more_enchantments.interfaces.IPersistentProjectileEntity;

import java.util.UUID;

import static stone926.mods.more_enchantments.nbt.CustomNbtKeys.PERSISTENT_PROJECTILE.*;

@Mixin(PersistentProjectileEntity.class)
public abstract class PersistentProjectileEntityMixin extends Entity implements IPersistentProjectileEntity {

  @Shadow
  protected boolean inGround;
  private int traction = 0;
  private int disappearCountdown = -1;
  private UUID entityDamaged = null;
  private UUID entityNotDamaged = null;
  private boolean isFromEnch = false;
  private boolean forceDamage = false;

  public PersistentProjectileEntityMixin(EntityType<?> type, World world) {
    super(type, world);
  }

  @Override
  public boolean isFromEnch() {
    return isFromEnch;
  }

  @Override
  public void setFromEnch(boolean fromEnch) {
    isFromEnch = fromEnch;
  }

  @Override
  public int getTraction() {
    return traction;
  }

  @Override
  public void setTraction(int traction) {
    this.traction = traction;
  }

  @Override
  public int getDisappearCountdown() {
    return disappearCountdown;
  }

  @Override
  public void setDisappearCountdown(int disappearCountdown) {
    this.disappearCountdown = disappearCountdown;
  }

  @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/projectile/PersistentProjectileEntity;isNoClip()Z"))
  private void tick1(CallbackInfo ci) {
    if (disappearCountdown > 0 && inGround) --disappearCountdown;
    if (disappearCountdown == 0) discard();
  }

  @Override
  public UUID getEntityNotDamage() {
    return entityNotDamaged;
  }

  @Override
  public void setEntityNotDamage(UUID uuid) {
    entityNotDamaged = uuid;
  }

  @Override
  public UUID getEntityDamage() {
    return entityDamaged;
  }

  @Override
  public void setEntityDamage(UUID uuid) {
    this.entityDamaged = uuid;
  }

  @Override
  public void setForceDamage(boolean forceDamage) {
    this.forceDamage = forceDamage;
  }

  @Override
  public boolean shouldForceDamage() {
    return forceDamage;
  }

  @Inject(method = "readCustomDataFromNbt", at = @At("HEAD"))
  protected void readCustomDataFromNbt(NbtCompound nbt, CallbackInfo ci) {
    if (nbt.contains(DISAPPEAR_COUNTDOWN)) setDisappearCountdown(nbt.getInt(DISAPPEAR_COUNTDOWN));
    if (nbt.contains(TRACTION)) setTraction(nbt.getInt(TRACTION));
    if (nbt.contains(FROM_ENCH)) setFromEnch(nbt.getBoolean(FROM_ENCH));
    if (nbt.contains(FORCE_DAMAGE)) setForceDamage(nbt.getBoolean(FORCE_DAMAGE));
    if (nbt.contains(ENTITY_DAMAGE)) setEntityDamage(nbt.getUuid(ENTITY_DAMAGE));
    if (nbt.contains(ENTITY_NOT_DAMAGE)) setEntityNotDamage(nbt.getUuid(ENTITY_NOT_DAMAGE));
  }

  @Inject(method = "writeCustomDataToNbt", at = @At("HEAD"))
  protected void writeCustomDataToNbt(NbtCompound nbt, CallbackInfo ci) {
    nbt.putInt(DISAPPEAR_COUNTDOWN, getDisappearCountdown());
    nbt.putInt(TRACTION, getTraction());
    nbt.putBoolean(FROM_ENCH, isFromEnch());
    nbt.putBoolean(FORCE_DAMAGE, shouldForceDamage());
    if (getEntityDamage() != null) nbt.putUuid(ENTITY_DAMAGE, getEntityDamage());
    if (getEntityNotDamage() != null) nbt.putUuid(ENTITY_NOT_DAMAGE, getEntityNotDamage());
  }

  @Inject(method = "onHit", at = @At("HEAD"))
  private void traction(LivingEntity target, CallbackInfo ci) {
    if (getTraction() > 0) {
      Vec3d vec3d = this.getVelocity().multiply(1.0D, 0.0D, 1.0D).normalize().multiply(getTraction() * 0.7D);
      if (vec3d.lengthSquared() > 0.0D) {
        Vec3d knockback = StableEnchantment.getKnockback(-vec3d.x, 0.1D, -vec3d.z, EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.STABLE, target));
        target.addVelocity(knockback.x, knockback.y, knockback.z);
      }
    }
  }

  @Inject(method = "onEntityHit", at = @At("HEAD"))
  private void onEntityHit(EntityHitResult entityHitResult, CallbackInfo ci) {
    Entity victim = ((ProjectileEntity) (Object) this).getOwner();
    if (victim instanceof LivingEntity l && l.getEntityWorld() instanceof ServerWorld server) {
      int targetOfAllLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.THE_TARGET_OF_ALL, l);
      int tenThousandArrowsAtShooterLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.TEN_THOUSAND_ARROWS_AT_SHOOTER, l);
      int tenThousandArrowsAtTargetLvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.TEN_THOUSAND_ARROWS_AT_TARGET, l);
      if (!isFromEnch()) {
        TheTargetOfAllEnchantment.spawnArrowsNearEntity(server, l, entityHitResult.getEntity(), (PersistentProjectileEntity) (Object) this, targetOfAllLvl);
        TenThousandArrowsEnchantment.spawnArrowsNearEntity(server, l, entityHitResult.getEntity(), (PersistentProjectileEntity) (Object) this, tenThousandArrowsAtShooterLvl, ArrowSpawnPosition.SHOOTER);
        TenThousandArrowsEnchantment.spawnArrowsNearEntity(server, l, entityHitResult.getEntity(), (PersistentProjectileEntity) (Object) this, tenThousandArrowsAtTargetLvl, ArrowSpawnPosition.TARGET);
      }
    }
  }

  @Redirect(method = "onEntityHit", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;addVelocity(DDD)V"))
  private void handleStableEnchantment(LivingEntity instance, double x, double y, double z) {
    int lvl = EnchantmentHelper.getEquipmentLevel(MoreEnchantmentsMod.STABLE, instance);
    if (lvl > 0) {
      Vec3d knockback = StableEnchantment.getKnockback(x, y, z, lvl);
      instance.addVelocity(knockback.x, knockback.y, knockback.z);
    } else {
      instance.addVelocity(x, y, z);
    }
  }

  @Redirect(method = "onEntityHit", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;damage(Lnet/minecraft/entity/damage/DamageSource;F)Z"))
  private boolean damage(Entity entity, DamageSource source, float amount) {
    if (entity.getUuid().equals(getEntityNotDamage())) {
      discard();
      return false;
    } else {
      if (isFromEnch()) {
        if (getEntityNotDamage() != null) return tryForceDamage(entity, source, amount);
        else if (entity.getUuid().equals(getEntityDamage())) return tryForceDamage(entity, source, amount);
        else {
          discard();
          return false;
        }
      } else return tryForceDamage(entity, source, amount);
    }
  }

}
